package strung

import (
	"sync"
	"time"

	"starsine.cn/appTpl/cmn"
	"starsine.cn/appTpl/cmn/config/aconf"
	"starsine.cn/appTpl/cmn/logs"
	"starsine.cn/appTpl/cmn/timermgr"
)

type StrungMng struct {
	strungSize int
	data       map[string]*Strung

	tmID int64
	tm   *timermgr.TimerMgr

	sync.RWMutex
}

func CreateStrungMng() *StrungMng {
	p := new(StrungMng)

	p.strungSize = aconf.GetInt("SvcLog.Strung.Size", 100, aconf.WithENV("svclog_strung_size"))
	p.data = make(map[string]*Strung)

	p.tmID = -1
	p.tm = cmn.GetTm()

	return p
}

func (p *StrungMng) Start() {
	p.checkPeriod()
}

func (p *StrungMng) End() {
	p.tm.CancelTimer(p.tmID)
}

func (p *StrungMng) checkAndRecycle(params interface{}) {
	p.Recycle()
}

func (p *StrungMng) checkPeriod() {
	p.tmID = p.tm.AddPeriodTimer(
		time.Duration(10),
		timermgr.NewOnTimeOut(p.checkAndRecycle,
			nil,
		))
}

func (p *StrungMng) Get(k string) *Strung {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if v, ok := p.data[k]; ok {
		return v
	}

	v := CreateStrung(p.strungSize)
	v.Start()
	p.data[k] = v

	logs.Debugf("StrungMng get(%s)", k)
	return v
}

func (p *StrungMng) Push(k string) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if v, ok := p.data[k]; ok {
		v.End()
		delete(p.data, k)
		logs.Debugf("StrungMng push(%s)", k)
	} else {
		logs.Errorf("deleting an unexist Strung(%s)", k)
	}
}

func (p *StrungMng) Recycle() {
	for k, v := range p.data {
		if v.IsExpired() {
			p.Push(k)
		}
	}
}
